/**
 * @file MStreamer.c
 *
 * This file implements the main process of the application.
 *
 * @author Luc Fourestier
 * @date 2009
 *
 * @mainpage MStreamer
 *
 * The MStreamer is a tool to receive, store and send Transport Stream (MPEG).
 * The user can select the different input and output type it wants (file, UDP, TCP...) and so
 * have a flexible streaming pipe.
 *
 * @section MStreamer_Intf The user's guide
 *
 * There are two possible interfaces: a command option interface and a CLI.
 *
 * @subsection MStreamer_Intf_Opt The command option
 *
 * As many shell tools, while starting you can specify options using '-'.
 * -h will help you to know more about the potential options you can pass.
 *
 * @subsection MStreamer_Intf_Cli The CLI
 *
 * The MSStreamer implements a basic shell interface, for real time commands.
 * A prompt "cmd>" is shown.
 * Type 'help' to get more up to date information about the potential commands.
 *
 * But typically here is what you can do:
 *
 *	Usage: MStreamer [options]
 *
 *	Example: MStreamer -o 10.0.0.1
 *
 *	Options:
 *		@li -o  address - our own internet address.
 *		@li -x  port - The XML-RPC listen port.
 *		@li -h - Display this message.
 *
 *	Commands:
 *		@li quit - Close the streams and quit application.
 *		@li help - Display this message.
 *		@li o address - Our own internet address.
 *		@li record session address port file - Start a recording session.
 *		@li play session address port file(s) - Start a playback session (list of files are coma-separated, ex: "file1.ts,file2.ts").
 *		@li stop session - Stop a session.
 *		@li stopall - Stop all sessions.
 *		@li l session - List a session.
 *		@li li session - List a session with intermediate details.
 *		@li ll session - List a session in more details.
 *		@li la - List all sessions.
 *		@li lia - List all sessions with intermediate details.
 *		@li lla - List all sessions in more details.
 *		@li ls level - List all sessions at level.
 *
 *	Note:
 *		@li In the list (see command "la", etc..), the star '*' indicates the file currently played in the playlist. Ex: "store.ts,*test.mpeg" means test.mpeg is the current file.
 *
 * @section MStreamer_DevGui The developper's guide
 *
 * The MStreamer is based on the architecture and design described in @ref Arch.
 *
 */


#include "MStreamer.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <bits/signum.h>

#include "StreamAnalyser.h"
#include "StreamReader.h"
#include "StreamPlayer.h"
#include "StreamPipe.h"
#include "TS.h"

// Globals
MStreamerOpt_t MStreamerOpt;
char * Version = VERSION;

// statics
static void PrintHelp(char * Name);
static void PrintVersion(void);
static int signalHandlerSetup(void);
static void Finalize(void);

static int Loop = 1;

/**
 * The main function
 *
 * @param argc: Argument number
 * @param argv: Argument list
 * @return -1 if error else 0
 */
int main(int argc, char *argv[])
{
    int Ret = 0;
    opterr = 0;
    struct timeval RefTime;
    char * AppName = APP_NAME;

    // Set the reference time for all the rest of the application
    gettimeofday(&RefTime, NULL);

    // Set address
    strncpy(MStreamerOpt.OwnInetAddr, "0.0.0.0", MSTR_IP_ADDR_LENGTH);

    // Set defaults
#ifdef CLI_ENABLE
    MStreamerOpt.CliEnable = 1;
#endif
    MStreamerOpt.Daemonize = 0;

    // Extract the application name
    if ((argv[0][0] == '.') && (argv[0][1] == '/'))
    {
    	AppName = &argv[0][2];
    }
    else
    {
    	AppName = argv[0];
    }

    // Setup signals
    signalHandlerSetup();

    // Parse option
     while ((Ret = getopt (argc, argv, "o:hvd")) != -1)
     {
 		switch (Ret)
 		{
        case 'd':
#ifdef CLI_ENABLE
            MStreamerOpt.CliEnable = 0;
#endif
            MStreamerOpt.Daemonize = 1;
        break;
		case 'o':
			if (optarg) strncpy(MStreamerOpt.OwnInetAddr, optarg, MSTR_IP_ADDR_LENGTH);
		break;

        case 'h':
            PrintHelp(AppName);
            return(0);
        case 'v':
            PrintVersion();
            return(0);
 		break;

 		default:
 			break;
 		}
     }

     // Daemonize
     if (MStreamerOpt.Daemonize)
     {
         Ret = daemon(1, 0);
         if (Ret == -1)
         {
             return(-1);
         }
     }


#ifdef CLI_ENABLE
	if (MStreamerOpt.CliEnable)
	{
        // Command input
		char CommandName[MSTR_MAX_NAME_LENGTH];
		int CmdNum = 0;
		while (1)
		{
			// Get the function name
			printf("\ncmd> ");
			usleep(300000);
			CmdNum = scanf("%s", CommandName);

			if (CmdNum > 0)
			{
				CmdNum--;

				// HELP
				if (strcmp("help", CommandName) == 0)
				{
					PrintHelp(AppName);
					continue;
				}

				// HELP
				if (strcmp("version", CommandName) == 0)
				{
					PrintVersion();
					continue;
				}

				// QUIT
				if (strcmp("quit", CommandName) == 0)
				{
					break;
				}

				// Our own address
				if (strcmp("o", CommandName) == 0)
				{
					CmdNum = scanf("%s", MStreamerOpt.OwnInetAddr);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					printf("OwnInetAddr changed to %s\n", MStreamerOpt.OwnInetAddr);

					continue;
				}

				// Record
				if (strcmp("record", CommandName) == 0)
				{
					SPI_Option_t SPIOption;

					CmdNum = scanf("%s %s %i %s", SPIOption.SessionID, SPIOption.Address, &SPIOption.Port, SPIOption.FileName[0]);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					strncpy(SPIOption.OwnAddress, MStreamerOpt.OwnInetAddr, IP_ADDR_LENGTH);
					SPIOption.MaxCBSize = MAX_CIRCBUFF_ELT;
					SPIOption.ReferenceTime = RefTime;
					SPIOption.SessionType = Record;

					Ret = SPI_Create(NULL, SPIOption);
					if (Ret != OK)
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot create record session: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
						continue;
					}

					continue;
				}

				// Play
				if (strcmp("play", CommandName) == 0)
				{
					SPI_Option_t SPIOption;
					unsigned int i = 0, j = 0, k = 0;
					char FileNames[SR_MAX_FILE*SPI_MAX_NAME_LENGTH];

					CmdNum = scanf("%s %s %i %s", SPIOption.SessionID, SPIOption.Address, &SPIOption.Port, FileNames);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					FileNames[(SR_MAX_FILE*SPI_MAX_NAME_LENGTH)-1] = 0; // Force NULL termination

					i = 0;
					j = 0;
					k = 0;
					while ((FileNames[i] != 0) && (i < (SR_MAX_FILE*SPI_MAX_NAME_LENGTH)))
					{
						if (FileNames[i] == ',')
						{
							if (k >= SR_MAX_FILE)
							{
								break;
							}

							FileNames[i] = 0;
							strncpy(SPIOption.FileName[k], FileNames+j, SPI_MAX_NAME_LENGTH);
							k++;
							j = i+1;
						}

						i++;
					}

					if ((FileNames[i] == 0) || (i >= (SR_MAX_FILE*SPI_MAX_NAME_LENGTH)))
					{
						strncpy(SPIOption.FileName[k], FileNames+j, SPI_MAX_NAME_LENGTH);
					}

					strncpy(SPIOption.OwnAddress, MStreamerOpt.OwnInetAddr, IP_ADDR_LENGTH);
					SPIOption.MaxCBSize = MAX_CIRCBUFF_ELT;
					SPIOption.ReferenceTime = RefTime;
					SPIOption.SessionType = Playback;

					Ret = SPI_Create(NULL, SPIOption);
					if (Ret != OK)
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot create playback session: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Stop session
				if (strcmp("stop", CommandName) == 0)
				{
					char SessionID[MSTR_MAX_NAME_LENGTH];

					CmdNum = scanf("%s", SessionID);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					Ret = SPI_DeleteID(SessionID);
					if (Ret != OK)
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot stop that session: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Stop all sessions
				if (strcmp("stopall", CommandName) == 0)
				{
					Ret = SPI_DeleteList();
					if (Ret != OK)
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot delete all SPI: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Print a session
				if (strcmp("l", CommandName) == 0)
				{
					char SessionID[MSTR_MAX_NAME_LENGTH];

					CmdNum = scanf("%s", SessionID);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					Ret = SPI_PrintID(SessionID, PRINT_LEVEL_BASIC, stdout);
					if (Ret != OK)
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list that session: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Print a session
				if (strcmp("li", CommandName) == 0)
				{
					char SessionID[MSTR_MAX_NAME_LENGTH];

					CmdNum = scanf("%s", SessionID);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					Ret = SPI_PrintID(SessionID, PRINT_LEVEL_INTERMEDIATE, stdout);
					if (Ret != OK)
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list that session: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Print a session in more details
				if (strcmp("ll", CommandName) == 0)
				{
					char SessionID[MSTR_MAX_NAME_LENGTH];

					CmdNum = scanf("%s", SessionID);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					Ret = SPI_PrintID(SessionID, PRINT_LEVEL_INTERNALS, stdout);
					if (Ret != OK)
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list that session: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Print all sessions at basic level
				if (strcmp("la", CommandName) == 0)
				{
					Ret = SPI_PrintList(PRINT_LEVEL_BASIC, stdout);
					if ((Ret != OK) && (Ret != SPI_ERROR_EMPTY_LIST))
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list all SPI: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Print all sessions at intermediate level
				if (strcmp("lia", CommandName) == 0)
				{
					Ret = SPI_PrintList(PRINT_LEVEL_INTERMEDIATE, stdout);
					if ((Ret != OK) && (Ret != SPI_ERROR_EMPTY_LIST))
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list all SPI: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Print all sessions at internal level
				if (strcmp("lla", CommandName) == 0)
				{
					Ret = SPI_PrintList(PRINT_LEVEL_INTERNALS, stdout);
					if ((Ret != OK) && (Ret != SPI_ERROR_EMPTY_LIST))
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list all SPI: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

				// Print all sessions at level
				if (strcmp("ls", CommandName) == 0)
				{
					int level = 0;

					CmdNum = scanf("%i", &level);
					if (CmdNum <= 0)
					{
						printf("Invalid parameter\n");
						continue;
					}

					Ret = SPI_PrintList(level, stdout);
					if ((Ret != OK) && (Ret != SPI_ERROR_EMPTY_LIST))
					{
						snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list all SPI: Error %i\n", Ret);
						printf("%s", MStreamerOpt.ErrorString);
					}

					continue;
				}

			   // Print all sessions at internal level
				if (strcmp("pla", CommandName) == 0)
				{
					FILE * TmpFile = fopen("sessions.info", "w+");
					if (TmpFile != NULL)
					{
						Ret = SPI_PrintList(PRINT_LEVEL_FULL, TmpFile);
						if ((Ret != OK) && (Ret != SPI_ERROR_EMPTY_LIST))
						{
							snprintf(MStreamerOpt.ErrorString, MSTR_MAX_NAME_LENGTH, "Cannot list all SPI: Error %i\n", Ret);
							printf("%s", MStreamerOpt.ErrorString);
						}
						fclose(TmpFile);
					}

					continue;
				}

			// If we reach this line, we either have a bad input or we did not find any matching commands
			printf("Invalid command\n");
			}
		}
    }
    else
    {
        while (Loop)
        {
            sleep(10);
        }
    }
#else
    while (Loop)
    {
        sleep(10);
    }
#endif

	Finalize();

	printf("Bye bye!\n");

	return 0;
}

/**
 * Close everything
 */
static void Finalize(void)
{
	SPI_DeleteList();
}

/**
 * Handles signals
 * @param signum: Signal number
 */
void signalHandler(int signum)
{
	switch (signum)
    {
    case SIGTERM:
    case SIGKILL:
    case SIGINT:
        Loop = 0;
#ifdef CLI_ENABLE
        if (MStreamerOpt.CliEnable)
        {
            close(STDIN_FILENO); // Kill scanf
        }
#endif
        break;
    }
}

/**
 * Setup signal handler
 * @return -1 if error, 0 else
 */
static int signalHandlerSetup(void)
{
    if ((signal(SIGTERM, &signalHandler) == SIG_ERR) || (signal(SIGINT, &signalHandler) == SIG_ERR))
    {
        return -1;
    }

    return 0;
}


/**
 * Print the usage and help
 *
 * @param Name (in): The name of the application
 */
static void PrintHelp(char * Name)
{
	if (Name == NULL)
	{
		Name = "MStreamer";
	}

	printf(	"%1$s receives/analyzes/transmits streams.\n"
			"\n"
			"\tUsage: %1$s [options]\n"
			"\tExample: %1$s -o 10.0.0.1 \n"
			"\n"
			"\tOptions:\n"
            "\t\t-d                Daemonize the application (no CLI anymore).\n"
			"\t\t-o  address       our own internet address.\n"
            "\t\t-h                Display this message.\n"
            "\t\t-v                Display the version (Release number).\n"
			"\n"
			"\tCommands:\n"
			"\t\tquit              Close the streams and quit application.\n"
			"\t\thelp              Display this message.\n"
	        "\t\tversion           Display the version (Release number).\n"
			"\t\to address         Our own internet address.\n"
			"\t\trecord session address port file	Start a recording session.\n"
			"\t\tplay session address port file(s) 	Start a playback session (list of files are coma-separated, ex: \"file1.ts,file2.ts\").\n"
			"\t\tstop session      Stop a session.\n"
			"\t\tstopall           Stop all sessions.\n"
			"\t\tl session         List a session.\n"
			"\t\tli session        List a session with intermediate details.\n"
			"\t\tll session        List a session in more details.\n"
			"\t\tla                List all sessions.\n"
			"\t\tlia               List all sessions with intermediate details.\n"
			"\t\tlla               List all sessions in more details.\n"
			"\t\tls level          List all sessions at level.\n"
			"\t\tpla               Print into sessions.info, all sessions in more details.\n"
			"\n"
			"\tNote:\n"
			"\t\tIn the list (see command \"la\", etc..), the star \'*\' indicates the file currently played in the playlist. "
			"Ex: \"store.ts,*test.mpeg\" means test.mpeg is the current file.\n",
			Name
			);
}

/**
 * Print the version of MStreamer
 */
static void PrintVersion(void)
{
    printf( "Version: %1$s\n"
            "Author: Luc Fourestier\n"
            "Copyright: 2010\n",
            Version
            );
}

